import os
import pandas as pd
import sys
from pathlib import Path
import datetime
import tkinter as tk
from tkinter import messagebox, simpledialog, filedialog, ttk
import traceback
import threading
import time

class ExcelMerger:
    """Excel文件合并工具类"""
    
    def __init__(self, root=None):
        """初始化合并工具"""
        self.root = root
        self.progress = None
        self.progress_label = None
        self.progress_window = None
        self.cancel_flag = False
        
        # 获取脚本所在目录路径
        if getattr(sys, 'frozen', False):
            # 如果是打包后的可执行文件
            self.current_dir = os.path.dirname(sys.executable)
        else:
            # 如果是正常运行的脚本
            self.current_dir = os.path.dirname(os.path.abspath(__file__))
        
        # 创建output文件夹（如果不存在）
        self.output_dir = os.path.join(self.current_dir, "output")
        if not os.path.exists(self.output_dir):
            os.makedirs(self.output_dir)
    
    def create_progress_window(self):
        """创建进度条窗口"""
        self.progress_window = tk.Toplevel(self.root)
        self.progress_window.title("Excel合并进度")
        self.progress_window.geometry("400x150")
        self.progress_window.resizable(False, False)
        
        # 窗口居中
        self.progress_window.update_idletasks()
        width = self.progress_window.winfo_width()
        height = self.progress_window.winfo_height()
        x = (self.progress_window.winfo_screenwidth() // 2) - (width // 2)
        y = (self.progress_window.winfo_screenheight() // 2) - (height // 2)
        self.progress_window.geometry(f'{width}x{height}+{x}+{y}')
        
        # 设置进度标签
        self.progress_label = ttk.Label(self.progress_window, text="准备中...", font=("SimHei", 10))
        self.progress_label.pack(pady=10)
        
        # 设置进度条
        self.progress = ttk.Progressbar(self.progress_window, orient="horizontal", length=350, mode="determinate")
        self.progress.pack(pady=10)
        
        # 取消按钮
        cancel_button = ttk.Button(self.progress_window, text="取消", command=self.cancel_merge)
        cancel_button.pack(pady=5)
        
        # 阻止关闭窗口
        self.progress_window.protocol("WM_DELETE_WINDOW", lambda: None)
        
        # 更新UI
        self.progress_window.update()
    
    def cancel_merge(self):
        """取消合并操作"""
        if messagebox.askyesno("确认", "确定要取消合并操作吗？"):
            self.cancel_flag = True
            self.update_progress_label("正在取消操作...")
    
    def update_progress(self, value, maximum=100):
        """更新进度条"""
        if self.progress:
            self.progress["value"] = value
            self.progress["maximum"] = maximum
            self.progress_window.update()
    
    def update_progress_label(self, text):
        """更新进度标签"""
        if self.progress_label:
            self.progress_label["text"] = text
            self.progress_window.update()
    
    def close_progress_window(self):
        """关闭进度窗口"""
        if self.progress_window:
            self.progress_window.destroy()
            self.progress_window = None
            self.progress = None
            self.progress_label = None
    
    def select_excel_files(self):
        """选择要合并的Excel文件"""
        files = filedialog.askopenfilenames(
            title="选择要合并的Excel文件",
            filetypes=[("Excel文件", "*.xlsx *.xls")],
            initialdir=self.current_dir
        )
        return list(files) if files else []
    
    def get_all_excel_files(self):
        """获取当前目录下所有Excel文件"""
        excel_files = []
        for file in os.listdir(self.current_dir):
            # 排除临时文件和已合并的结果
            if file.endswith(('.xlsx', '.xls')) and not file.startswith('~$') and not file.startswith('合并结果'):
                excel_files.append(os.path.join(self.current_dir, file))
        return excel_files
    
    def check_file_accessibility(self, file_path):
        """检查文件是否可访问"""
        try:
            # 尝试以只读方式打开文件
            with open(file_path, 'rb') as f:
                return True
        except (PermissionError, IOError):
            return False
    
    def merge_excel_files(self, selected_mode="auto"):
        """
        合并Excel文件
        
        Args:
            selected_mode: 选择模式，"auto"自动合并所有文件，"manual"手动选择文件
        """
        # 根据模式获取要合并的文件
        if selected_mode == "manual":
            excel_files = self.select_excel_files()
        else:
            excel_files = self.get_all_excel_files()
        
        if not excel_files:
            messagebox.showinfo("提示", "没有找到Excel文件进行合并")
            return False
        
        # 创建进度条窗口
        self.create_progress_window()
        
        # 在新线程中执行合并操作
        merge_thread = threading.Thread(target=self._merge_process, args=(excel_files,))
        merge_thread.daemon = True
        merge_thread.start()
        
        return True
    
    def _merge_process(self, excel_files):
        """执行合并过程（在单独线程中运行）"""
        try:
            # 重置取消标志
            self.cancel_flag = False
            
            # 创建一个空的DataFrame用于存储合并结果
            merged_df = pd.DataFrame()
            
            # 显示处理文件数量
            total_files = len(excel_files)
            success_files = 0
            failed_files = 0
            
            self.update_progress_label(f"开始处理 {total_files} 个Excel文件...")
            self.update_progress(0, total_files)
            
            # 遍历每个Excel文件并合并
            for i, file_path in enumerate(excel_files):
                # 检查是否取消
                if self.cancel_flag:
                    self.close_progress_window()
                    messagebox.showinfo("已取消", "合并操作已被用户取消")
                    return
                
                file_name = os.path.basename(file_path)
                
                # 更新进度
                self.update_progress(i, total_files)
                self.update_progress_label(f"正在处理: {file_name} ({i+1}/{total_files})")
                
                try:
                    # 检查文件是否可访问
                    if not self.check_file_accessibility(file_path):
                        print(f"警告: 无法访问文件 {file_name}，可能正在被其他程序使用")
                        failed_files += 1
                        continue
                    
                    # 检查文件大小，避免处理超大文件导致内存问题
                    file_size_mb = os.path.getsize(file_path) / (1024 * 1024)
                    if file_size_mb > 100:  # 大于100MB的文件给出警告
                        if not messagebox.askyesno("警告", 
                                              f"文件 {file_name} 大小为 {file_size_mb:.1f}MB，处理可能较慢。\n是否继续？"):
                            print(f"跳过大文件: {file_name}")
                            continue
                    
                    # 读取Excel文件中的所有sheet
                    excel_data = pd.read_excel(file_path, sheet_name=None)
                    
                    # 检查是否成功读取到数据
                    if not excel_data:
                        print(f"警告: {file_name} 没有包含任何数据")
                        failed_files += 1
                        continue
                    
                    file_df = pd.DataFrame()  # 存储当前文件的所有sheet
                    
                    for sheet_name, df in excel_data.items():
                        # 检查是否取消
                        if self.cancel_flag:
                            break
                            
                        # 跳过空白sheet
                        if df.empty:
                            print(f"警告: {file_name} 中的 {sheet_name} 表单为空")
                            continue
                        
                        # 添加源文件和sheet信息列
                        df['源文件'] = file_name
                        df['表单名称'] = sheet_name
                        
                        # 将当前sheet的DataFrame添加到当前文件的DataFrame
                        file_df = pd.concat([file_df, df], ignore_index=True)
                    
                    if not file_df.empty and not self.cancel_flag:
                        # 将当前文件的DataFrame合并到结果中
                        merged_df = pd.concat([merged_df, file_df], ignore_index=True)
                        success_files += 1
                        print(f"成功合并: {file_name}")
                    else:
                        if not self.cancel_flag:
                            print(f"警告: {file_name} 未包含有效数据")
                            failed_files += 1
                
                except Exception as e:
                    print(f"合并文件 {file_name} 时出错: {str(e)}")
                    failed_files += 1
                    # 记录详细错误信息到日志
                    with open(os.path.join(self.output_dir, "error_log.txt"), "a", encoding="utf-8") as f:
                        f.write(f"\n[{datetime.datetime.now()}] 处理 {file_name} 时出错:\n")
                        f.write(traceback.format_exc())
            
            # 关闭进度窗口
            self.close_progress_window()
            
            if self.cancel_flag:
                return
            
            if merged_df.empty:
                messagebox.showinfo("提示", "没有成功合并任何数据")
                return
            
            # 生成带时间戳的输出文件名
            timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            output_file = os.path.join(self.output_dir, f"合并结果_{timestamp}.xlsx")
            
            # 保存合并结果到新的Excel文件
            try:
                # 显示保存进度
                save_window = tk.Toplevel(self.root)
                save_window.title("正在保存")
                save_window.geometry("300x100")
                save_window.resizable(False, False)
                
                # 窗口居中
                save_window.update_idletasks()
                width = save_window.winfo_width()
                height = save_window.winfo_height()
                x = (save_window.winfo_screenwidth() // 2) - (width // 2)
                y = (save_window.winfo_screenheight() // 2) - (height // 2)
                save_window.geometry(f'{width}x{height}+{x}+{y}')
                
                save_label = ttk.Label(save_window, text="正在保存合并结果，请稍候...", font=("SimHei", 10))
                save_label.pack(pady=20)
                
                # 阻止关闭窗口
                save_window.protocol("WM_DELETE_WINDOW", lambda: None)
                save_window.update()
                
                # 保存文件
                merged_df.to_excel(output_file, index=False)
                
                # 关闭保存窗口
                save_window.destroy()
                
                # 计算统计信息
                total_rows = len(merged_df)
                total_columns = len(merged_df.columns)
                
                # 显示结果统计
                result_msg = (
                    f"合并完成！\n"
                    f"总文件数: {total_files}\n"
                    f"成功合并: {success_files}\n" 
                    f"合并失败: {failed_files}\n"
                    f"合并结果: {total_rows} 行 x {total_columns} 列\n"
                    f"输出文件: {output_file}"
                )
                
                messagebox.showinfo("成功", result_msg)
                
                # 询问是否打开输出文件夹
                if messagebox.askyesno("询问", "是否打开输出文件夹？"):
                    # 跨平台打开文件夹
                    if sys.platform == 'win32':
                        os.startfile(self.output_dir)
                    elif sys.platform == 'darwin':  # macOS
                        os.system(f'open "{self.output_dir}"')
                    else:  # Linux
                        os.system(f'xdg-open "{self.output_dir}"')
                
            except Exception as e:
                messagebox.showerror("保存错误", f"保存合并文件时出错:\n{str(e)}")
                # 记录详细错误信息到日志
                with open(os.path.join(self.output_dir, "error_log.txt"), "a", encoding="utf-8") as f:
                    f.write(f"\n[{datetime.datetime.now()}] 保存文件时出错:\n")
                    f.write(traceback.format_exc())
        
        except Exception as e:
            self.close_progress_window()
            messagebox.showerror("错误", f"合并过程中发生错误:\n{str(e)}")
            # 记录详细错误信息到日志
            with open(os.path.join(self.output_dir, "error_log.txt"), "a", encoding="utf-8") as f:
                f.write(f"\n[{datetime.datetime.now()}] 合并过程中发生错误:\n")
                f.write(traceback.format_exc())

def create_main_window():
    """创建主窗口"""
    root = tk.Tk()
    root.title("Excel合并工具")
    root.geometry("400x300")
    root.resizable(False, False)
    
    # 窗口居中
    root.update_idletasks()
    width = root.winfo_width()
    height = root.winfo_height()
    x = (root.winfo_screenwidth() // 2) - (width // 2)
    y = (root.winfo_screenheight() // 2) - (height // 2)
    root.geometry(f'{width}x{height}+{x}+{y}')
    
    # 创建Excel合并器实例
    merger = ExcelMerger(root)
    
    # 创建标题标签
    title_label = ttk.Label(root, text="Excel合并工具", font=("SimHei", 16, "bold"))
    title_label.pack(pady=20)
    
    # 创建说明标签
    desc_label = ttk.Label(root, text="将多个Excel文件合并为一个文件", font=("SimHei", 10))
    desc_label.pack(pady=5)
    
    # 创建按钮框架
    button_frame = ttk.Frame(root)
    button_frame.pack(pady=20)
    
    # 自动合并按钮
    auto_merge_button = ttk.Button(
        button_frame, 
        text="合并当前文件夹中的所有Excel文件", 
        command=lambda: merger.merge_excel_files("auto"),
        width=30
    )
    auto_merge_button.pack(pady=10)
    
    # 选择文件合并按钮
    select_merge_button = ttk.Button(
        button_frame, 
        text="选择要合并的Excel文件", 
        command=lambda: merger.merge_excel_files("manual"),
        width=30
    )
    select_merge_button.pack(pady=10)
    
    # 打开输出文件夹按钮
    open_folder_button = ttk.Button(
        button_frame, 
        text="打开输出文件夹", 
        command=lambda: open_output_folder(merger.output_dir),
        width=30
    )
    open_folder_button.pack(pady=10)
    
    # 创建底部标签
    footer_label = ttk.Label(root, text="© 2024 Excel合并工具", font=("SimHei", 8))
    footer_label.pack(side=tk.BOTTOM, pady=10)
    
    return root

def open_output_folder(output_dir):
    """打开输出文件夹"""
    # 如果文件夹不存在，则创建
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    # 跨平台打开文件夹
    if sys.platform == 'win32':
        os.startfile(output_dir)
    elif sys.platform == 'darwin':  # macOS
        os.system(f'open "{output_dir}"')
    else:  # Linux
        os.system(f'xdg-open "{output_dir}"')

if __name__ == "__main__":
    # 创建主窗口
    root = create_main_window()
    
    # 进入主循环
    root.mainloop() 